#pragma once

#include "../common/httplib.h"
#include "../common/FileHelper.hpp"
#include "../common/Log.hpp"
#include "../common/ThreadPool.hpp"
#include "VirtualHost.hpp"
#include <jsoncpp/json/json.h>
#include <iostream>
#include <map>
#include <functional>
#include <memory>

namespace micromq
{
    class MicroMQController
    {
    public:
        using ptr = std::shared_ptr<MicroMQController>;
    public:
        MicroMQController(uint16_t port, std::string resourceDir, VirtualHost::ptr host)
            : _port(port)
            , _resourceDir(resourceDir)
            , _host(host)
        {
            serverInit();
        }

        void start()
        {
            ThreadPool::getInstance()->submit([this]() {
                _server.listen("0.0.0.0", _port);
            });
            logInfo("MicroMQController start...");
        }

        ~MicroMQController()
        {}
    private:
        void serverInit()
        {
            _server.set_base_dir(_resourceDir.c_str());
            _server.Get("/", [this](const httplib::Request& req, httplib::Response& resp) {
                resp.status = 200;
                resp.reason = "OK";
                char content[4096];
                FileHelper::readAll(_resourceDir + "/login.html", content);
                resp.set_content(content, "text/html; charset=utf-8");
            });

            _server.Get("/login.html", [this](const httplib::Request& req, httplib::Response& resp) {
                resp.status = 200;
                resp.reason = "OK";
                char content[4096];
                FileHelper::readAll(_resourceDir + "/login.html", content);
                resp.set_content(content, "text/html; charset=utf-8");
            });

            _server.Get("/manager.html", [this](const httplib::Request& req, httplib::Response& resp) {
                resp.status = 200;
                resp.reason = "OK";
                char content[1024 * 20];
                FileHelper::readAll(_resourceDir + "/manager.html", content);
                resp.set_content(content, "text/html; charset=utf-8");
            });

            _server.Get("/css/login.css", [this](const httplib::Request& req, httplib::Response& resp) {
                resp.status = 200;
                resp.reason = "OK";
                char content[4096];
                FileHelper::readAll(_resourceDir + "/css/login.css", content);
                resp.set_content(content, "text/css");
            });

            _server.Get("/css/manager.css", [this](const httplib::Request& req, httplib::Response& resp) {
                resp.status = 200;
                resp.reason = "OK";
                char content[1024 * 20];
                FileHelper::readAll(_resourceDir + "/css/manager.css", content);
                resp.set_content(content, "text/css");
            });

            _server.Get("/js/jquery.min.js", [this](const httplib::Request& req, httplib::Response& resp) {
                resp.status = 200;
                resp.reason = "OK";
                char content[1024 * 100];
                FileHelper::readAll(_resourceDir + "/js/jquery.min.js", content);
                resp.set_content(content, "application/javascript");
            });

            _server.Get("/js/manager.js", [this](const httplib::Request& req, httplib::Response& resp) {
                resp.status = 200;
                resp.reason = "OK";
                char content[1024 * 20];
                FileHelper::readAll(_resourceDir + "/js/manager.js", content);
                resp.set_content(content, "application/javascript");
            });

            _server.Post("/user/login", [this](const httplib::Request& req, httplib::Response& resp) {
                if(!req.has_param("username") || !req.has_param("password"))
                {
                    resp.status = 400;
                    resp.reason = "BadRequest";
                    Json::Value loginResp;
                    loginResp["code"] = 400;
                    loginResp["message"] = "用户名或密码错误";
                    resp.set_content(_jsonWriter.write(loginResp).c_str(), "application/json; charset=utf-8");
                    return;
                }
                std::string username = req.get_param_value("username");
                std::string password = req.get_param_value("password");
                if(!login(username, password))
                {
                    resp.status = 200;
                    resp.reason = "OK";
                    Json::Value loginResp;
                    loginResp["code"] = 400;
                    loginResp["message"] = "用户名或密码错误";
                    resp.set_content(_jsonWriter.write(loginResp).c_str(), "application/json; charset=utf-8");
                    return;
                }
                resp.status = 200;
                resp.reason = "OK";
                Json::Value loginResp;
                loginResp["code"] = 200;
                loginResp["message"] = "OK";
                resp.set_content(_jsonWriter.write(loginResp).c_str(), "application/json; charset=utf-8");
                logInfo("<manager> username: %s login...", username.c_str());
            });

            _server.Get("/manager/virtual-host", [this](const httplib::Request& req, httplib::Response& resp) {
                std::unordered_map<std::string, Exchange::ptr> exchanges = _host->getAllExchange();
                Json::Value getExchangeResp;
                getExchangeResp["code"] = 200;
                getExchangeResp["message"] = "OK";
                getExchangeResp["data"] = "VirtualHost";
                resp.set_content(_jsonWriter.write(getExchangeResp), "application/json; charset=utf-8");
                logInfo("<manager> get virtual host");
            });

            _server.Get("/manager/exchange", [this](const httplib::Request& req, httplib::Response& resp) {
                std::unordered_map<std::string, Exchange::ptr> exchanges = _host->getAllExchange();
                Json::Value getExchangeResp;
                getExchangeResp["code"] = 200;
                getExchangeResp["message"] = "OK";
                Json::Value exchangeArrayJson(Json::arrayValue);
                for(auto& [key, val] : exchanges)
                {
                    Json::Value exchangeJson;
                    exchangeJson["name"] = val->_name;
                    exchangeJson["type"] = val->_type;
                    exchangeJson["durable"] = val->_durable;
                    exchangeJson["autoDelete"] = val->_autoDelete;
                    exchangeArrayJson.append(exchangeJson);
                }
                getExchangeResp["data"] = exchangeArrayJson;
                resp.set_content(_jsonWriter.write(getExchangeResp), "application/json; charset=utf-8");
                logInfo("<manager> get all exchanges");
            });

            _server.Get("/manager/message-queue", [this](const httplib::Request& req, httplib::Response& resp) {
                std::unordered_map<std::string, MsgQueue::ptr> msgQueues = _host->getAllMsgQueue();
                Json::Value getMsgQueueResp;
                getMsgQueueResp["code"] = 200;
                getMsgQueueResp["message"] = "OK";
                Json::Value msgQueueArrayJson(Json::arrayValue);
                for(auto& [key, val] : msgQueues)
                {
                    Json::Value msgQueueJson;
                    msgQueueJson["name"] = val->_name;
                    msgQueueJson["durable"] = val->_durable;
                    msgQueueJson["exclusive"] = val->_exclusive;
                    msgQueueJson["autoDelete"] = val->_autoDelete;
                    msgQueueArrayJson.append(msgQueueJson);
                }
                getMsgQueueResp["data"] = msgQueueArrayJson;
                resp.set_content(_jsonWriter.write(getMsgQueueResp), "application/json; charset=utf-8");
                logInfo("<manager> get all message queues");
            });

            _server.Get("/manager/binding", [this](const httplib::Request& req, httplib::Response& resp) {
                BindingMap bindingMap = _host->getAllBinding();
                Json::Value getBindingResp;
                getBindingResp["code"] = 200;
                getBindingResp["message"] = "OK";
                Json::Value bindingArrayJson(Json::arrayValue);
                for(auto& [exchangeName, bindingInfo] : bindingMap)
                {
                    for(auto& [msgQueueName, binding] : bindingInfo)
                    {
                        Json::Value bindingJson;
                        bindingJson["exchangeName"] = binding->_exchangeName;
                        bindingJson["msgQueueName"] = binding->_msgQueueName;
                        bindingJson["bindingKey"] = binding->_bindingKey;
                        bindingJson["durable"] = binding->_durable;
                        bindingArrayJson.append(bindingJson);
                    }
                }
                getBindingResp["data"] = bindingArrayJson;
                resp.set_content(_jsonWriter.write(getBindingResp), "application/json; charset=utf-8");
                logInfo("<manager> get all bindings");
            });

            _server.Get("/manager/message", [this](const httplib::Request& req, httplib::Response& resp) {
                std::unordered_map<std::string, MessageCenter::ptr> centers = _host->getMessageCenters();
                Json::Value getMessageResp;
                getMessageResp["code"] = 200;
                getMessageResp["message"] = "OK";
                Json::Value centerArrayJson(Json::arrayValue);
                for(auto& [msgQueueName, center] : centers)
                {
                    Json::Value centerJson;
                    centerJson["msgQueueName"] = msgQueueName;
                    Json::Value messageArrayJson(Json::arrayValue);
                    std::list<MessagePtr> messages = center->getWaitConsumeMessages();
                    for(auto message : messages)
                    {
                        Json::Value messageJson;
                        messageJson["id"] = message->payload().properties().id().substr(0, 20) + "...";
                        messageJson["mode"] = message->payload().properties().mode();
                        messageJson["routingKey"] = message->payload().properties().routing_key();
                        std::string content = message->payload().content();
                        if(content.size() > 10) content = content.substr(0, 20) + "...";
                        messageJson["content"] = content;
                        messageArrayJson.append(messageJson);
                    }
                    centerJson["messages"] = messageArrayJson;
                    centerArrayJson.append(centerJson);
                }
                getMessageResp["data"] = centerArrayJson;
                resp.set_content(_jsonWriter.write(getMessageResp), "application/json; charset=utf-8");
                logInfo("<manager> get all bindings");
            });
        }

        bool login(std::string username, std::string password)
        {
            return username == "admin" && password == "admin";
        }
    private:
        uint16_t _port;
        std::string _resourceDir;
        httplib::Server _server;
        Json::Reader _jsonReader;
        Json::FastWriter _jsonWriter;
        VirtualHost::ptr _host;
    };
}